<template>
  <PageWrapper>
    <a-card>
      <div style="margin-bottom: 10px">
        <a-range-picker
          v-model:value="timeVal"
          :start-placeholder="$t('faultDiagnosis.startTime')"
          :end-placeholder="$t('faultDiagnosis.entTime')"
          format="YYYY-MM-DD HH:mm:ss"
          @openChange="fnGetChainList"
        />
      </div>
      <div>
        <a-table
          :data-source="tableList"
          :pagination="pagination"
          row-key="id"
          :columns="column"
          :customRow="showCallChainDetail"
          @change="handleTableChange"
        >
        </a-table>
      </div>
      <div style="height: 570px" v-if="selectTime.id" v-loading="detailLoading">
        <div ref="callChart" style="width: 100%; height: 100%"></div>
      </div>
    </a-card>
  </PageWrapper>
</template>

<script>
import * as echarts from 'echarts'
import dayjs from "dayjs";
import { api } from '@/api'
import PageWrapper from '@/components/PageWrapper.vue'
import { formatDateTime } from '@/components/time/timeFormat'
import {fnTimeToUse} from "../../../components/time/timeFormat";

const ColorTypes = ['#d95850', '#eb8146', '#ffb248', '#f2d643', '#ebdba4', '#fcce10', '#b5c334', '#1bca93']

const heightOfJson = json => {
  const recur = (item, level = 0) => {
    if ((item.children || []).length === 0) {
      return level
    }
    let maxLevel = level
    item.children.forEach(child => {
      const tempLevel = recur(child, level + 1)
      maxLevel = Math.max(maxLevel, tempLevel)
    })
    return maxLevel
  }
  return recur(json)
}
const filterJson = (json, id) => {
  if (id == null) {
    return json
  }
  const recur = (item, subId) => {
    if (item.id === subId) {
      return item
    }
    // eslint-disable-next-line no-restricted-syntax
    for (const child of item.children || []) {
      const temp = recur(child, subId)
      if (temp) {
        item.children = [temp]
        item.value = temp.value // change the parents' values
        return item
      }
    }
  }
  return recur(json, id) || json
}
const recursionJson = (jsonObj, id) => {
  const data = []
  const filteredJson = filterJson(structuredClone(jsonObj), id)
  const rootVal = filteredJson.value
  const rootStartTime = filteredJson.start_timestamp
  const recur = (item, start = 0, level = 0) => {
    const temp = {
      name: item.id,

      // [level, start_val, end_val, name, percentage]
      value: [level, start, start + item.value, item.name, (item.value / rootVal) * 100, item],
      itemStyle: {
        color: ColorTypes[level % 9]
      }
    }
    data.push(temp)

    // let endTimeStamp = item.start_timestamp; // 上一个兄弟节点的结束时间戳

    item.children?.forEach(child => {
      const prevStart = child.start_timestamp - rootStartTime // 计算当前格子开始坐标
      recur(child, prevStart, level + 1)
    })
  }
  recur(filteredJson)
  return data
}
const renderItem = (params, api) => {
  const level = api.value(0)
  const startY = api.coord([api.value(1), 0])[1] // 获取坐标轴画布Y轴坐标
  const start = api.coord([api.value(1), level]) // 获取该数据块开始的实际画布坐标
  const end = api.coord([api.value(2), level]) // 获取该数据块结束的实际画布坐标
  const height = ((api.size && api.size([0, 1])) || [0, 20])[1] // 获取当y轴为1时的高度；
  const y = start[1]

  // if (height > 50) {
  //   height = 50;
  //   y = 50 * level + startY;
  // }
  const width = end[0] - start[0] // 宽度
  return {
    type: 'rect',
    transition: ['shape'],
    shape: {
      // 图形左上角坐标
      x: start[0],
      y,
      width,
      height: height - 2 /* itemGap */,
      r: 2
    },
    style: {
      fill: api.visual('color')
    },
    emphasis: {
      style: {
        stroke: '#000'
      }
    },
    textConfig: {
      position: 'insideLeft'
    },
    textContent: {
      style: {
        text: api.value(3),
        fontFamily: 'Verdana',
        fill: '#000',
        width: width - 4,
        overflow: 'truncate',
        ellipsis: '..',
        truncateMinChar: 1
      },
      emphasis: {
        style: {
          stroke: '#000',
          lineWidth: 0.5
        }
      }
    }
  }
}

export default {
  name: 'callChainManage',
  components: { PageWrapper },
  data() {
    return {
      tableList: [],
      timeVal: [],
      column: [
        {
          dataIndex: 'startTimeStr',
          title: this.$t('faultDiagnosis.startTime')
        },
        {
          dataIndex: 'client_ip',
          title: this.$t('faultDiagnosis.clientIPAddress')

        },
        {
          dataIndex: 'server_ip',
          title: this.$t('faultDiagnosis.serverIP')
        },
        {
          dataIndex: 'server_port',
          title: this.$t('faultDiagnosis.serverPort')
        },
        {
          dataIndex: 'protocol',
          title: this.$t('faultDiagnosis.protocolType')
        },
        {
          dataIndex: 'req_type',
          title: this.$t('faultDiagnosis.requestType')
        },
        {
          dataIndex: 'req_status',
          title: this.$t('faultDiagnosis.requestStatus')
        },
        {
          dataIndex: 'responseDelay',
          title: this.$t('faultDiagnosis.responseLate')
        }
      ],
      query_params: {},
      selectTime: {
        id: ''
      },
      pagination: {
        current: 1,
        pageSize: 10,
        showTotal: total => `${this.$t('common.total', { count: total })}`,
        showSizeChanger: true,
        pageSizeOptions: ['10', '20', '30', '40']
      },
      detailLoading: false
    }
  },
  async mounted() {
    // 默认一小时
    this.timeVal = [dayjs().subtract(1, 'hours'), dayjs()]
    await this.fnGetChainList()
  },
  methods: {
    async fnGetChainList(status) {
      console.log(status)
      const param = {
        startTime: this.timeVal[0].format('x'),
        endTime: this.timeVal[1].format('x')
      }
      const [_, data] = await api.fnGetCallChainList(param)
      this.query_params = data.query_params
      if (!data.request_list){
        return
      }
      this.tableList = data.request_list
        .map(item => {
          item.startTime = Math.floor(item.start_timestamp / 10 ** 6) // ns 转换未为 ms
          item.startTimeStr = formatDateTime(item.startTime)
          item.req_method = item.msg.req_method
          item.resp_status = item.msg.resp_status
          item.responseDelay = fnTimeToUse(item.value / 10 ** 6)
          return item
        })
        .sort((a, b) => {
          return b.start_timestamp - a.start_timestamp
        })
    },
    handleTableChange(page) {
      page.current && (this.pagination.current = page.current)
      page.pageSize && (this.pagination.pageSize = page.pageSize)
      this.fnGetChainList()
    },
    async fnDraw(row) {
      this.detailLoading = true
      const param = {
        query_params: { ...this.query_params },
        id: row.id
      }
      const [_, res] = await api.fnGetCallChainData(param)
      this.detailLoading = false
      const callData = res
      const levelOfOriginalJson = heightOfJson(callData)

      const chart = echarts.init(this.$refs.callChart)
      const option = {
        backgroundColor: {
          type: 'linear',
          x: 0,
          y: 0,
          x2: 0,
          y2: 1,
          colorStops: [
            {
              offset: 0.05,
              color: '#eee'
            },
            {
              offset: 0.95,
              color: '#eeeeb0'
            }
          ]
        },
        tooltip: {
          formatter: params => {
            const samples = params.value[2] - params.value[1]
            return `${params.marker} ${params.value[3]}: (${echarts.format.addCommas(
              samples
            )} ns, ${+params.value[4].toFixed(2)}%, ${params.value[5].req_status})`
          }
        },
        grid: {
          top: 100,
          bottom: 50
        },
        dataZoom: [
          {
            // 鼠标缩放
            type: 'inside', // 内置型数据区域缩放组件
            filterMode: 'none'

            // start: 0, // 数据窗口范围的起始百分比
            // end: 100 // 数据窗口范围的结束百分比
          }
        ],
        title: [
          {
            text: 'Flame Graph',
            left: 'center',
            top: 10,
            textStyle: {
              fontFamily: 'Verdana',
              fontWeight: 'normal',
              fontSize: 20
            }
          }
        ],
        toolbox: {
          feature: {
            restore: {}
          },
          right: 20,
          top: 10
        },
        xAxis: {
          show: false
        },
        yAxis: {
          show: false,
          max: levelOfOriginalJson + 1,
          inverse: true
        },
        series: [
          {
            type: 'custom',
            renderItem,
            encode: {
              x: [0, 1, 2],
              y: 0
            },
            data: recursionJson(callData, null)
          }
        ]
      }

      chart.setOption(option, true)
      this.$nextTick(() => {
        chart.resize()
      })
      chart.on('click', params => {
        const newData = recursionJson(callData, params.data.name)
        chart.setOption({
          xAxis: {
            max: params.data.value[2],
            min: params.data.value[1]
          },
          series: [{ newData }]
        })
      })
    },
    showCallChainDetail(row) {
      return {
        onClick: () => {
          if (this.selectTime.id === row.id) {
            // 取消点击
            this.selectTime = {}
          } else {
            this.selectTime = row
            this.fnDraw(row)
          }
        }
      }
    }
  }
}
</script>

<style scoped></style>
